#add gold standard reference to link class?

class subject:
	def __init__(self, annotators, sbj, fname, thash):
		self.fname = fname
		self.thash = thash
		self.data = {}
		self.cmprnd = dict((annot, dict()) for annot in annotators)
		
	def add_annot(self, annot, sbj):
		"""Adds another annotator's tag to the subject."""
		for i in self.cmprnd[annot].keys():
			try:
				self.cmprnd[annot][str(i)] = str(sbj.attrib[i])
			except KeyError:
				self.cmprnd[annot][str(i)] = ''
		self.cmprnd[annot]['node_tag'] = sbj.tag
		
	def getAnnotators(self):
		"""Returns a list of annotators whose tags (or lack thereof) are included in the subject"""
		return self.cmprnd.keys()
		
	def getTags(self):
		"""Returns a list of the tags the subject has received"""
		return dict((annot, self.cmprnd[annot]['node_tag']) for annot in self.getAnnotators())
		
	def getComparanda(self, attrib, strict):
		if not strict:
			return self.getTags()
		else:
			try:
				if not attrib:
					attrib = set([i for val in self.cmprnd.values() for i in val.keys()])
				return dict((annot, Comparand(dict((att, self.cmprnd[annot][att]) for att in attrib))) for annot in self.getAnnotators())
			except:
				print "Inappropriate attribute name."
				raise
		
	def getFileName(self):
		return self.fname
		
	def agreement(self):
		try:
			vals = self.getTags().values()
			den = len(vals) * (len(vals) - 1)
			num = 0
			for i in set(vals):
				temp = sum([i == j for j in vals])
				num += (temp * (temp - 1))
			return float(num)/den
		except ZeroDivisionError:
			print "Insufficient annotators to assess agreement:", self.fname
			
	def __str__(self):
		try:
			return '\n'.join([str(key) + ': ' + str(val) for key,val in self.data.iteritems()])
		except:
			return str(self.data)

class extent(subject):
	def __init__(self, annotators, sbj, fname, thash):
		"""Initializes the extent with a list of annotators and empty annotations"""
		subject.__init__(self, annotators, sbj, fname, thash)
		temp = [sbj.attrib[i] for i in [u'text', u'start', u'end']]
		temp[1:] = [int(j) for j in temp[1:]]
		self.data = dict(zip(['text', 'start', 'end'], temp))
		for i in annotators:
			self.cmprnd[i] = dict([(str(key), '') for key in set(sbj.attrib.keys()).union(set(['node_tag'])).difference(set(self.data.keys() + [u'comment']))])
			
	def overlaps(self, ext2):
		"""Determines overlaps between extents"""
		s1, e1 = self.data['start'], self.data['end']
		s2, e2 = ext2.data['start'], ext2.data['end']
		return (self.thash == ext2.thash and all([e2 > s1, e1 > s2]))
			
	def __eq__(self, ext2):
		"""Determines whether extents exactly correspond"""
		return ((self.thash == ext2.thash and all([abs(self.data[i] - ext2.data[i]) <= 1 for i in ['start', 'end']])) and self.data['text'].strip() == ext2.data['text'].strip())
		
class link(subject):
	def __init__(self, annotators, sbj, fname, thash):
		"""Initializes the link with a list of annotators and empty annotations"""
		subject.__init__(self, annotators, sbj, fname, thash)
		self.data = dict(zip(['fromID', 'toID', 'fromText', 'toText'], [sbj.attrib[u'fromID'], sbj.attrib[u'toID'], sbj.attrib[u'fromText'], sbj.attrib[u'toText']]))
		for i in annotators:
			self.cmprnd[i] = dict([(str(key), '') for key in set(sbj.attrib.keys()).difference(set(self.data.keys() + [u'comment'])).union(set(['node_tag']))])
		
	def __eq__(self, link2):
		"""Determines whether two links correspond"""
		return (self.thash == link2.thash and all([self.data[i] == link2.data[i] for i in self.data.keys()]))
	
	def __ne__(self, link2):
		"""Opposes link.__eq__"""
		return not self.__eq__(link2)
		
class Comparand:
	def __init__(self, d):
		self.comp = tuple(sorted(list(d.iteritems())))
			
	def __eq__(self, cmp2):
		return self.comp == cmp2.comp
		
	def __ne__(self, cmp2):
		return not self.comp.__eq__(cmp2)
		
	def __hash__(self):
		return hash(self.comp)

